package com.pic.mgt.scheduler;

import java.text.ParseException;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.SchedulerException;
import org.quartz.SchedulerListener;
import org.quartz.Trigger;
import org.quartz.impl.StdScheduler;
import org.springframework.scheduling.quartz.JobDetailBean;
import org.springframework.scheduling.quartz.SimpleTriggerBean;

public class QuartzAutoJobImpl implements QuartzAutoJob {
	private StdScheduler stdScheduler;
	private SchedulerListener schedulerListener;
	
	private boolean isAddedSchedulerListener = false;
	
	@Override
	public void haltAutoJob() {
		this.stdScheduler.standby();
	}

	@Override
	public void removeScheduledJob(String groupName, String jobName)
										throws ScheduleAutoJobException {
		try {
			this.stdScheduler.unscheduleJob(jobName, groupName);
		} catch (SchedulerException e) {
			throw new ScheduleAutoJobException(
					"SUDA-313 Removing scheduled auto job(job name:'" + jobName + "') failed.", e);
		}
	}

	@Override
	public void rescheduleSimpleJob(Date newExecutionTime, String groupName,
							  String jobName) throws ScheduleAutoJobException {
		try {
			// throws SchedulerException
			Trigger trigger = this.stdScheduler.getTrigger(jobName, groupName);
			
			if (null != trigger) {
				trigger.setStartTime(newExecutionTime);
				
				// throws SchedulerException
				this.stdScheduler.rescheduleJob(jobName, groupName, trigger);
			}
			else {
				// 代表沒有找到該 Trigger
				throw new ScheduleAutoJobException(
						"SUDA-315 Rescheduling auto job(job name:'" + jobName + "' group name:'" + groupName + "') failed. Can't find this job.");
			}
		} catch (SchedulerException e) {
			throw new ScheduleAutoJobException(
					"Rescheduling auto job(job name:'" + jobName + "') failed.", e);
		}
	}

	@Override
	public void scheduleSimpleJob(Date executionTime, Class jobClass,
								  Map jobData, String jobName, String groupName)
						          throws ScheduleAutoJobException {
		// 建立一個 Job Detail
		JobDetailBean jobDetailBean = new JobDetailBean();
		jobDetailBean.setName(jobName);
		jobDetailBean.setGroup(groupName);
		jobDetailBean.setJobClass(jobClass);
		
		// 建立一個  Simple Trigger
		SimpleTriggerBean simpleTriggerBean = new SimpleTriggerBean();
		simpleTriggerBean.setStartTime(executionTime);
		simpleTriggerBean.setRepeatCount(0);
		simpleTriggerBean.setName(jobName);
		simpleTriggerBean.setGroup(groupName);
		simpleTriggerBean.setJobDataAsMap(jobData);
		
		try {
			this.stdScheduler.scheduleJob(jobDetailBean, simpleTriggerBean);
		} catch (SchedulerException e) {
			throw new ScheduleAutoJobException(
					"SUDA-314 Scheduling auto job(job name:'" + jobName + "' group name:'" + groupName + "') failed.", e);
		}
	}
	
	@Override
	public void scheduleCronJob (String cronExpression, Class jobClass,
								 Map jobData, String jobName, String groupName, String jobDescription) throws ScheduleAutoJobException {
		try {
			// 建立一個 Job Detail
			JobDetailBean jobDetailBean = new JobDetailBean();
			jobDetailBean.setName(jobName);
			jobDetailBean.setGroup(groupName);
			jobDetailBean.setJobClass(jobClass);
			
			// 建立一個  Cron Trigger
			CronTrigger cronTrigger = new CronTrigger();
			// throws ParseException
			cronTrigger.setCronExpression(cronExpression);
			cronTrigger.setName(jobName);
			cronTrigger.setGroup(groupName);
			if (null != jobDescription) {
				cronTrigger.setDescription(jobDescription);
			}
			
			JobDataMap jobDataMap = new JobDataMap(jobData);
			cronTrigger.setJobDataMap(jobDataMap);
			
			// 加入排程
			// throws SchedulerException
			this.stdScheduler.scheduleJob(jobDetailBean, cronTrigger);
		} catch (ParseException e) {
			throw new ScheduleAutoJobException(
					"SUDA-314 Scheduling auto job(job name:'" + jobName + "' group name:'" + groupName + "') failed.", e);
		} catch (SchedulerException e) {
			throw new ScheduleAutoJobException(
					"SUDA-314 Scheduling auto job(job name:'" + jobName + "' group name:'" + groupName + "') failed.", e);
		}
	}
	
	@Override
	public void rescheduleCronJob(String cronExpression, String groupName,
			String jobName, String jobDescription) throws ScheduleAutoJobException {
		try {
			// throws SchedulerException
			CronTrigger cronTrigger = (CronTrigger)this.stdScheduler.getTrigger(jobName, groupName);
			
			// 若沒有此 Job 則拋 error
			if (null == cronTrigger) {
				throw new ScheduleAutoJobException(
						"SUDA-315 Rescheduling auto job(job name:'" + jobName + "' group name:'" + groupName + "') failed. Can't find this job.");
			}
			
			// throws ParseException
			if (null != cronExpression) {
				cronTrigger.setCronExpression(cronExpression);
			}
			if (null != jobDescription) {
				cronTrigger.setDescription(jobDescription);
			}
			
			// throws SchedulerException
			this.stdScheduler.rescheduleJob(jobName, groupName,cronTrigger);
		} catch (SchedulerException e) {
			throw new ScheduleAutoJobException(
					"SUDA-315 Rescheduling auto job(job name:'" + jobName + "' group name:'" + groupName + "') failed.", e);
		} catch (ParseException e) {
			throw new ScheduleAutoJobException(
					"SUDA-315 Rescheduling auto job(job name:'" + jobName + "' group name:'" + groupName + "') failed.", e);
		}
	}
	
	@Override
	public List<Trigger> getTriggersByGroupName(String triggerGroupName) throws ScheduleAutoJobException {
		List<Trigger> triggerList = new LinkedList<Trigger>();
		
		try {
			String[] triggersNameArray = this.stdScheduler.getTriggerNames(triggerGroupName);
			
			for (String triggersName : triggersNameArray) {
				triggerList.add(this.stdScheduler.getTrigger(triggersName, triggerGroupName));
			}
		} catch (SchedulerException e) {
			throw new ScheduleAutoJobException(
					"SUDA-316 Getting '" + triggerGroupName + "' group triggers failed.", e);
		}
		
		return triggerList;
	}

	@Override
	public void shutdownAutoJob() {
		this.stdScheduler.shutdown(true);
	}

	@Override
	public void startAutoJob() throws ScheduleAutoJobException {
		try {
			if (!this.isAddedSchedulerListener) {
				this.stdScheduler.addSchedulerListener(this.schedulerListener);
				this.isAddedSchedulerListener = true;
			}
			
			this.stdScheduler.start();
		} catch (SchedulerException e) {
			throw new ScheduleAutoJobException(
					"Start auto job failed.", e);
		}
	}
	
	@Override
	public boolean isHalted() {
		return this.stdScheduler.isInStandbyMode();
	}
	
	@Override
	public boolean isShutdown() {
		return this.stdScheduler.isShutdown();
	}
	
	@Override
	public boolean isStarted() {
		return this.stdScheduler.isStarted();
	}
	
	@Override
	public int getTriggerState(String triggerName, String triggerGroupName)
			throws ScheduleAutoJobException {
		try {
			return this.stdScheduler.getTriggerState(triggerName, triggerGroupName);
		} catch (SchedulerException e) {
			throw new ScheduleAutoJobException(
					"SUDA-328 Getting trigger '" + triggerName + "' (group:'" + triggerGroupName + "') status failed.", e);
		}
	}
	
	@Override
	public void pauseTrigger(String triggerName, String triggerGroup)
			throws ScheduleAutoJobException {
		try {
			this.stdScheduler.pauseTrigger(triggerName, triggerGroup);
		} catch (SchedulerException e) {
			throw new ScheduleAutoJobException(
					"SUDA-333 Pausing trigger '" + triggerName + "' (group:'" + triggerGroup + "') failed.", e);
		}
	}
	
	@Override
	public void pauseTriggerGroup(String triggerGroup)
			throws ScheduleAutoJobException {
		try {
			this.stdScheduler.pauseTriggerGroup(triggerGroup);
		} catch (SchedulerException e) {
			throw new ScheduleAutoJobException(
					"SUDA-334 Pausing trigger group '" + triggerGroup + "' failed.", e);
		}
	}
	
	@Override
	public void resumeTrigger(String triggerName, String triggerGroup)
			throws ScheduleAutoJobException {
		try {
			this.stdScheduler.resumeTrigger(triggerName, triggerGroup);
		} catch (SchedulerException e) {
			throw new ScheduleAutoJobException(
					"SUDA-335 Resuming trigger '" + triggerName + "' (group:'" + triggerGroup + "') failed.", e);
		}
	}
	
	@Override
	public void resumeTriggerGroup(String triggerGroup)
			throws ScheduleAutoJobException {
		try {
			this.stdScheduler.resumeTriggerGroup(triggerGroup);
		} catch (SchedulerException e) {
			throw new ScheduleAutoJobException(
					"SUDA-336 Resuming trigger group '" + triggerGroup + "' failed.", e);
		}
	}
	
	// --- getter and seter ---
	
	public void setStdScheduler(StdScheduler stdScheduler) {
		this.stdScheduler = stdScheduler;
	}

	public void setSchedulerListener(SchedulerListener schedulerListener) {
		this.schedulerListener = schedulerListener;
	}
}
